/*
 * Copyright (c) 2003-2010, Live Media Pty. Ltd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *  3. Neither the name of Live Media nor the names of its contributors may be used to endorse
 *     or promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.colony.networkvm.machine;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.argot.TypeException;
import com.argot.TypeLibraryWriter;
import com.argot.TypeMap;
import com.argot.TypeOutputStream;
import com.argot.TypeWriter;
import com.argot.common.UInt16;
import com.argot.common.UVInt28;
import com.argot.meta.MetaAbstract;
import com.argot.remote.MetaRemoteException;
import com.colony.networkvm.instruction.NvmInstruction;
import com.colony.remote.RemoteException;

public class NvmState
{
	public static final String TYPENAME = "nvm.state";

	// items moved between systems.
	protected List _instructions;
	protected int _programCounter;
	protected NvmHeap _heap;
	protected Throwable _exception;

	public NvmState( int programCounter, NvmInstruction[] instructions, NvmHeap heap, Throwable exception )
	{
		_programCounter = programCounter;
		_instructions = new ArrayList();
		for(int x=0;x<instructions.length;x++)
		{
			_instructions.add(instructions[x]);
		}
		_heap = heap;
		_exception = exception;
	}
	
	public NvmState()
	{
		_instructions = new ArrayList();
		_heap = new NvmHeap();
		_programCounter = 0;
		_exception = null;	
	}

	public void setException( Throwable exception )
	{
		_exception = exception;
	}
	
	private static class NvmStateWriter
	implements TypeWriter
	{
		private MetaAbstract _exAbstract;
		private TypeWriter _uint16;
		private TypeWriter _uvint28;
		private TypeWriter _nvmInstructions;
		private TypeWriter _nvmHeap;
		private TypeWriter _remoteException;
		private Integer _remoteEmptyException;

		public NvmStateWriter(TypeMap map) 
		throws TypeException
		{
			_exAbstract = (MetaAbstract) map.getStructure(map.getStreamId("remote.exception"));
			_uint16 = map.getWriter(map.getStreamId(UInt16.TYPENAME));
			_uvint28 = map.getWriter(map.getStreamId(UVInt28.TYPENAME));
			_nvmInstructions = map.getWriter(map.getStreamId("nvm.instructions"));
			_nvmHeap = map.getWriter(map.getStreamId("nvm.heap"));
			_remoteException = map.getWriter(map.getStreamId(MetaRemoteException.TYPENAME));
			_remoteEmptyException = new Integer(map.getStreamId("empty"));
		}
		
		public void write(TypeOutputStream out, Object o) 
		throws TypeException, IOException
		{
			NvmState state = (NvmState)o;
			
			_uint16.write( out, new Integer( state._programCounter ));
			_nvmInstructions.write( out, state._instructions.toArray() );
			_nvmHeap.write( out, state._heap );
			if ( state._exception == null )
			{
				_uvint28.write( out, _remoteEmptyException );
			}
			else
			{
				int id;
				
	            try
	            {
	                int[] ids = out.getTypeMap().getLibrary().getId( state._exception.getClass());
	                if (ids.length!=1) throw new TypeException("Class bound to multiple types");
	                id = ids[0];

	                String name = out.getTypeMap().getLibrary().getName( id ).getFullName();
	                int id2 = out.getTypeMap().getLibrary().getTypeId(name);
	                System.out.println("id = " + id + " id2 = " + id2 );
	                id = id2;
	            }
	            catch (TypeException e)
	            {
	            	id = -1;
	            }
	            //out.getTypeMap().getLibrary().getT
	            // Need to check if id is name id or definition id.
	            if ( id != -1 )
	            {
	            }
	            
				if ( id == -1 || !_exAbstract.isMapped( id ))
				{
					// exception isn't mapped.  So wrap it.
					RemoteException ex = new RemoteException( state._exception.getMessage(), state._exception );					
					_remoteException.write( out, ex );
				}
				else
				{
					_remoteException.write( out, state._exception );
				}
			}
		}
	}
	
	public static class NvmStateLibraryWriter
	implements TypeLibraryWriter
	{
		public TypeWriter getWriter(TypeMap map) 
		throws TypeException 
		{
			return new NvmStateWriter(map);
		}
		
	}

}
